//////////////////////////////////////////////////
// STUDENT RESEARCH PROJECT						//
// UNIVERSITY OF HAGEN 2012						//
// File:	WeatherDataProcessor.cpp			//
// Date:	27.08.2012							//
// Author:	Max Pejs							//
//////////////////////////////////////////////////

#include "WeatherDataProcessor.h"
#include "DataContainer.h"

#ifndef WIN32
// FOR ARDUINO ONLY
#include "RS232Trace.h"
#endif

extern void GetReadyForNextFrame();
extern void StopBitPolling();
extern unsigned long millis();

WeatherDataProcessor::WeatherDataProcessor()
{
	state = SOF;
	tempStorage = 0;
	bitCnt = 0;
	sendDataLastTimerCnt = 0;
}

WeatherDataProcessor::~WeatherDataProcessor(){}

void WeatherDataProcessor::ProcessBit(UInt8 newBit)
{
	// mask last bit and copy to bigger data type to avoid overflow
	int temp = newBit;

	// move bit to right position
	temp = temp << bitCnt;

	// copy bit to storage
	tempStorage = tempStorage | temp;

	switch(state)
	{
	case SOF:		/*****************************************/

		// if BitCnt not reached upper limit level (bit length of SOF)
		if(bitCnt <5)
		{
			// increment receive bit counter
			bitCnt++;
		}
		else
		{
			// shift all bits and wait for nex bit
			tempStorage = tempStorage >> 1;
		}

		// make sure there is no overflow over 5 Bit
		tempStorage = tempStorage & 0x1F;

		// Check transition condition to the next state
		// if we received 5 bits already an the received value is 0x04
		if(tempStorage == (~SOF_VALUE &0x1f) && (bitCnt == 5))
		{
			state = DATA;		// switch state
			tempStorage = 0;	// reset temporal storage
			bitCnt = 0;			// reset bit counter
		}
		break;

	case DATA:		/*****************************************/

		bitCnt++;

		// Check transition condition to the next state
		if( bitCnt == (WIND_DIR_BIT_LENGTH + WIND_SPEED_FIELD_LENGTH) )
		{
			// data received completely
			// invert and mask 4 LSB bits 
			recvDataSet.WindDirection = (~tempStorage) & 0x0F;

			// shift 4 bits left, then invert and mask 9 LSB bits 
			recvDataSet.WindSpeed = (~(tempStorage >> 4)) & 0x0FFF;

			tempStorage = 0;	// reset temporal storage
			bitCnt = 0;			// reset bit counter
			state = CHECKSUM;	// switch state
		}

		break;

	case CHECKSUM:	/*****************************************/
		bitCnt++;
		if(bitCnt == CHECKSUM_FIELD_LENGTH)
		{
			recvDataSet.CheckSum = (~tempStorage) & 0x000F;
			state = DATA_INV;	// switch state
			tempStorage = 0;	// reset temporal storage
			bitCnt = 0;			// reset bit counter
		}
		break;

	case DATA_INV: /*****************************************/
		bitCnt++;
		if( bitCnt == (WIND_DIR_BIT_LENGTH + WIND_SPEED_FIELD_LENGTH) )
		{
			// data received completely
			// stop bit polling (for avoiding pushing unuseful zeros in ring buffer)
			StopBitPolling();

			// read inverted data
			recvDataSet.InvWindDirection = (~tempStorage) & 0x0F;
			recvDataSet.InvWindSpeed = ~((tempStorage >> 4) & 0x0FFF);

			if(FrameIsOk())
			{
				// frame is ok, prepare data set for send to RS232
				lastCorrectRecvDataSet = recvDataSet;
				STATISTIC.FrameReceived(true);
			}
			else
			{
				// if not, last data set will be sent to RS232
				STATISTIC.FrameReceived(false);
			}

			
			GetReadyForNextFrame();

			tempStorage = 0;	// reset temporal storage
			bitCnt = 0;			// reset bit counter
			state = SOF;		// switch state
		}
		break;

	default:		/*****************************************/
		state = SOF;	// reset
		break;
	}
}

UInt8 WeatherDataProcessor::FrameIsOk()
{
	unsigned char dataFrameOk;

	// calculate check sum from received data
	UInt16 calcCheckSum =  (recvDataSet.WindDirection & 0x0F) + 
						   (recvDataSet.WindSpeed & 0x000F ) + 
						   ((recvDataSet.WindSpeed>>4) & 0x000F ) + 
						   ((recvDataSet.WindSpeed>>8) & 0x000F );

	calcCheckSum = calcCheckSum & 0x000F;

	// compare check sum, wind direction and wind speed
	dataFrameOk =	(calcCheckSum				==    recvDataSet.CheckSum)						&&
					(recvDataSet.WindDirection	== ((~recvDataSet.InvWindDirection) & 0x0F))	&&
					(recvDataSet.WindSpeed		== ((~recvDataSet.InvWindSpeed)	    & 0x01FF));
	
	return dataFrameOk;
}

void WeatherDataProcessor::SendData(WeatherDataSet dataToSend)
{
	UInt8 speedPreDecPointPos, speedDecimalPlace;

	UInt8 windDirBuffer[4];		// wind direction (max size 3 chars + char '0') f.e. "ENE"0
	UInt8 speedBuffer[6];		// speed direction (max size 5 chars + char '0') f.e. "168.0"0
	
	// wind direction
	WriteDirectionToBuffer(dataToSend.WindDirection, windDirBuffer);

	// wind speed
	speedPreDecPointPos = dataToSend.WindSpeed / 10;
	speedDecimalPlace = dataToSend.WindSpeed % 10;

	// write pre decimal point value to buffer
#ifdef WIN32
	_itoa_s(speedPreDecPointPos, (char *)speedBuffer, 6, 10);
#else
	itoa(speedPreDecPointPos, (char *)speedBuffer, 10);
#endif

	// write point delimiter
	UInt8 len = Utility::calcStrLen( (UInt8*) speedBuffer);
	speedBuffer[len++] = '.';

	// write post decimal point value to buffer
#ifdef WIN32
	_itoa_s(speedDecimalPlace, ((char *)speedBuffer) + len, 6 - len, 10);
#else
	itoa(speedDecimalPlace, ((char *)speedBuffer) + len, 10);
#endif

	Message msg;
	msg.WriteDataField(windDirBuffer);
	msg.WriteDataField(speedBuffer);
	msg.SendMessage();
}

void WeatherDataProcessor::WriteDirectionToBuffer(UInt16 windDir, UInt8 * dirPtr)
{
	char * tmp;
	unsigned char * tmp2 = dirPtr;

	switch(windDir)
	{
	case 0: tmp = "N"; break;
	case 1: tmp = "NNE"; break;
	case 2: tmp = "NE"; break;
	case 3: tmp = "ENE"; break;
	case 4: tmp = "E"; break;
	case 5: tmp = "ESE"; break;
	case 6: tmp = "SE"; break;
	case 7: tmp = "SSE"; break;
	case 8: tmp = "S"; break;
	case 9: tmp = "SSW"; break;
	case 10: tmp = "SW"; break;
	case 11: tmp = "WSW"; break;
	case 12: tmp = "W"; break;
	case 13: tmp = "WNW"; break;
	case 14: tmp = "NW"; break;
	case 15: tmp = "NNW"; break;
	default: tmp = "---"; break;
	}

	while(*tmp != 0)
	{
		*dirPtr++ = *tmp++;
	}

	*(dirPtr) = 0;
}

void WeatherDataProcessor::SendData(void)
{
	unsigned long currTimerVal = millis();

	if( (currTimerVal - sendDataLastTimerCnt) > TRANSMISSION_DELAY)
	{
		SendData(lastCorrectRecvDataSet);
		sendDataLastTimerCnt = currTimerVal;
	}
}